package com.cruzr.opengles.map;

import android.content.Context;
import android.graphics.Color;
import android.graphics.PointF;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

import com.cruzr.opengles.map.layer.GridLayer;

public class GLMapView extends GLSurfaceView implements SurfaceHolder.Callback {

    private final static String TAG = "GLMapView";

    public static final int EVENT_NONE = 0;
    public static final int EVENT_DRAG = 1;
    public static final int EVENT_ZOOM = 2;

    private PointF mPrePoint;
    private PointF mPreOnePoint;
    private PointF mCurOnePoint;
    private PointF mPreTwoPoint;
    private PointF mCurTwoPoint;

    public int mEventMode = EVENT_NONE;
    private GLMapRender mRender;

    public GLMapView(Context context) {
        super(context);
        init(context);
    }

    public GLMapView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public void init(Context context) {
        setEGLContextClientVersion(2);
        setEGLConfigChooser(8, 8, 8, 8, 16, 0);
        getHolder().setFormat(Color.rgb(GridLayer.INT_R_BG, GridLayer.INT_G_BG, GridLayer.INT_B_BG));
        setZOrderOnTop(true);
        setZOrderMediaOverlay(true);

        mRender = new GLMapRender(context);
        setRenderer(mRender);
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

        mPrePoint = new PointF(0.0f, 0.0f);
        mPreOnePoint = new PointF(0.0f, 0.0f);
        mPreTwoPoint = new PointF(0.0f, 0.0f);
        mCurOnePoint = new PointF(0.0f, 0.0f);
        mCurTwoPoint = new PointF(0.0f, 0.0f);
    }

    public void loadMap(int width, int height, byte[] pixels) {
        mRender.updateMap(width, height, pixels);
        requestRender();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int eventAction = event.getAction();
        Log.i(TAG, "onTouchEvent -> eventAction : " + eventAction);
        switch (eventAction) {
            case MotionEvent.ACTION_DOWN:
                mEventMode = EVENT_DRAG;
                mPrePoint.set(event.getX(), event.getY());
                mPreOnePoint.set(event.getX(), event.getY());
                break;

            case MotionEvent.ACTION_POINTER_DOWN | 0x0100:
                mEventMode = EVENT_ZOOM;
                mPreOnePoint.set(event.getX(0), event.getY(0));
                mPreTwoPoint.set(event.getX(1), event.getY(1));
                break;

            case MotionEvent.ACTION_MOVE:
                if (mEventMode == EVENT_DRAG) {
                    if (event.getPointerCount() == 1) {
                        mRender.setTranslate(event.getX() - mPrePoint.x, event.getY() - mPrePoint.y);
                        mPrePoint.set(event.getX(), event.getY());
                    }
                } else if (mEventMode == EVENT_ZOOM) {
                    if (event.getPointerCount() == 2) {
                        mCurOnePoint.set(event.getX(0), event.getY(0));
                        mCurTwoPoint.set(event.getX(1), event.getY(1));

                        float preDistance = getPointDistance(mPreOnePoint, mPreTwoPoint);
                        float curDistance = getPointDistance(mCurOnePoint, mCurTwoPoint);

                        float scale = curDistance / preDistance;
                        mRender.setScale(mRender.getScale() * (float) Math.sqrt(scale));

                        float preAngle = getPointAngle(mPreOnePoint, mPreTwoPoint);
                        float curAngle = getPointAngle(mCurOnePoint, mCurTwoPoint);
                        mRender.setIncreaseAngle(preAngle - curAngle);

                        mPreOnePoint.set(mCurOnePoint.x, mCurOnePoint.y);
                        mPreTwoPoint.set(mCurTwoPoint.x, mCurTwoPoint.y);
                    }
                }
                break;

            case MotionEvent.ACTION_UP:
                if (mEventMode == EVENT_DRAG) {
                    PointF pointF = new PointF(event.getX(), event.getY());
                    float distance = getPointDistance(pointF, mPreOnePoint);
                    if (distance < 10) {
                        mRender.setPoint(event.getX(), event.getY());
                    }
                }
                mPreOnePoint.set(0, 0);
                mPreTwoPoint.set(0, 0);
                mEventMode = EVENT_NONE;
                break;

            default:
                mEventMode = EVENT_NONE;
                break;
        }

        mRender.computeMVP();
        requestRender();
        return true;
    }

    protected float getPointDistance(PointF pointA, PointF pointB) {
        double dis;
        double xdis = Math.pow(pointA.x - pointB.x, 2.0);
        double ydis = Math.pow(pointA.y - pointB.y, 2.0);
        dis = Math.sqrt(xdis + ydis);
        return (float) dis;
    }

    protected float getPointAngle(PointF pointA, PointF pointB) {
        double angle;
        float x = pointA.x - pointB.x;
        float y = pointA.y - pointB.y;
        if (x == 0) {
            if (y > 0) {
                angle = 90f;
            } else if (y < 0) {
                angle = -90f;
            } else {
                angle = 0;
            }
        } else if (x > 0) {
            angle = Math.atan(y / x) * 180 / Math.PI;
        } else {
            angle = 180 + Math.atan(y / x) * 180 / Math.PI;
        }
        return (float) angle;
    }

    public void onDestroy() {
        if (mRender != null) {
            mRender.onDestroy();
        }
    }
}
